define([
    'app',
    'clipboard',
    'text!text/markdown-syntax.md',
    'ace-language-tools'
], function(app, Clipboard, markdownSyntax) {
    app.controller('MarkdownEditorController', function($http, $scope, $compile, $timeout, $showdown, xUtil,
            xDialog, toastr, $location, $routeParams) {
        var self = this;
        self.code = '';
        self.fullScreen = false;
        self.fullScreen2 = false;

        var codeEditor = null,
            mdOptions = $showdown.getOptions();

        var rheader1 = null,
            rheader2 = null,
            rheader = null;

        var filename = 'README.md',
            blogid = $routeParams.blogid,
            modified = null;

        self.init = function() {
            new Clipboard('#copy', {
                text: function() {
                    return self.code;
                }
            }).on('success', function(e) {
                toastr.success('已复制到剪贴板！');
            });

            rheader1 = (mdOptions.smoothLivePreview ?
                    '^(.+)[ \t]*\n={2,}[ \t]*\n+' : '^(.+)[ \t]*\n=+[ \t]*\n+');
            rheader2 = (mdOptions.smoothLivePreview ?
                    '^(.+)[ \t]*\n-{2,}[ \t]*\n+' : '^(.+)[ \t]*\n-+[ \t]*\n+');
            rheader = (mdOptions.requireSpaceBeforeHeadingText ?
                    '^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+' : '^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+');

            $timeout(function() {
                // code editor
                codeEditor = ace.edit('mdcode-editor');
                codeEditor.setOptions({
                    mode: 'ace/mode/markdown',
                    fontSize: 13,
                    showPrintMargin: false,
                    autoScrollEditorIntoView: true,
                    enableBasicAutocompletion: true,
                    enableSnippets: true,
                    enableLiveAutocompletion: true
                });
                codeEditor.on('change', function(e) {
                    $timeout(function() {
                        self.code = codeEditor.getValue();
                    })
                });

                // code editor tools
                jQuery('#mdcode-editor .ace_scroller').append($compile(jQuery('#editor-tools').html())($scope));
                jQuery('.x-editor-tool').draggable({
                    cursor: 'move',
                    handle: '.x-toolbar',
                    drag: function() {
                        jQuery(this).css('right', 'auto');
                    }
                }).mousedown(function(e) {
                    return false;
                });

                var jqEditorScope = jQuery('.x-editor').parents('.x-page-scope'),
                    jqGotoTop = jQuery('.x-editor-tool.x-goto-top');

                var timeout = null,
                    syncScroll = function() {
                        var startRow = codeEditor.getFirstVisibleRow(),
                            endRow = codeEditor.getLastVisibleRow(),
                            content = '',
                            headerId = null,
                            headerIds = null;
                            headerIdCount = 0;
                        for (var row = startRow; row <= endRow; row++) {
                            content = codeEditor.getSession().getLines(startRow, row).join('\n');
                            headerIds = getHeaderIds(content.trim());
                            if (!_.isEmpty(headerIds)) {
                                headerId = headerIds[0];
                                // lookup same previous header
                                if (startRow > 1) {
                                    content = codeEditor.getSession().getLines(0, startRow - 1).join('\n');
                                    headerIds = getHeaderIds(content.trim(), true);
                                    headerIdCount = _.size(_.filter(headerIds, function(id) {
                                            return id === headerId;
                                        }));
                                    if (headerIdCount > 0) {
                                        headerId += '-' + headerIdCount;
                                    }
                                }
                                // scroll preview
                                var jqHeader = jQuery('#' + headerId);
                                if (jqHeader.length > 0) {
                                    jQuery('.x-markdown').stop(true).animate({
                                        scrollTop: jQuery('.x-markdown').scrollTop() + jqHeader.position().top
                                    }, 200);
                                    break;
                                }
                            }
                        }
                    };
                codeEditor.getSession().on('changeScrollTop', function(scrollTop) {
                    if (scrollTop > 50) {
                        jqGotoTop.removeClass('hidden');
                    } else {
                        jqGotoTop.addClass('hidden');
                    }
                    clearTimeout(timeout);
                    // match and scroll
                    timeout = setTimeout(syncScroll, 200);
                });
                // full screen
                $scope.$watch('vm.fullScreen', function() {
                    setTimeout(function() {
                        codeEditor.resize();
                        jQuery('.x-editor-tool').css('left', '').css('top', '').css('right', '');
                    }, 50);
                });
                var scrollTop = 0;
                $scope.$watch('vm.fullScreen2', function(newValue) {
                    if (newValue) {
                        scrollTop = jQuery('.x-markdown').scrollTop();
                        jQuery('.x-markdown').stop(true).scrollTop(0);
                    } else {
                        jQuery('.x-markdown').stop(true).scrollTop(scrollTop);
                    }
                });

                if (blogid) {
                    // load blog
                    $http.get('/app/blog/articles/' + blogid + '.md').then(function(response) {
                        if (response.status == 200) {
                            codeEditor.setValue(response.data, -1);
                            codeEditor.resize();
                            // get blog title
                            $http.get('/app/blog/blogs.json').then(function(response) {
                                var articles = response.data.articles;
                                for (var i = 0; i < articles.length; i++) {
                                    if (articles[i].id == blogid) {
                                        filename = articles[i].title.replace(/[\?\*:"<>\\\/\|]+/g, '').trim() + '.md';
                                        modified = articles[i].modified;
                                        break;
                                    }
                                }
                            });
                        }
                    });
                } else {
                    // load syntax of markdown
                    $timeout(function() {
                        codeEditor.setValue(markdownSyntax, -1);
                        codeEditor.resize();
                    });
                }
            });
        }

        function getHeaderIds(content, global) {
            var headerId = null,
                headerIds = [];

            var rxHeader1 = new RegExp(rheader1, 'm' + (global ? 'g' : '')),
                rxHeader2 = new RegExp(rheader2, 'm' + (global ? 'g' : '')),
                rxHeader = new RegExp(rheader, 'm' + (global ? 'g' : ''));

            if (rxHeader1.test(content)) {
                content = content.replace(rxHeader1, function (wholeMatch, m1) {
                    headerId = getHeaderId(m1);
                    headerIds.push(headerId);
                    return '';
                });
            } else if (rxHeader2.test(content)) {
                content = content.replace(rxHeader2, function (matchFound, m1) {
                    headerId = getHeaderId(m1);
                    headerIds.push(headerId);
                    return '';
                });
            } else if (rxHeader.test(content)) {
                content = content.replace(rxHeader, function (wholeMatch, m1, m2) {
                    headerId = getHeaderId(m2);
                    headerIds.push(headerId);
                    return '';
                });
            }

            if (global && headerId != null) {
                headerIds = _.flatten(headerIds, getHeaderIds(content, global));
            }

            return headerIds;
        }

        function getHeaderId(title) {
            if (mdOptions.ghCompatibleHeaderId) {
                title = title
                  .replace(/ /g, '-')
                  .replace(/&amp;/g, '')
                  .replace(/¨T/g, '')
                  .replace(/¨D/g, '')
                  .replace(/[&+$,\/:;=?@"#{}|^¨~\[\]`\\*)(%.!'<>]/g, '')
                  .toLowerCase();
            } else if (mdOptions.rawHeaderId) {
                title = title
                  .replace(/ /g, '-')
                  .replace(/&amp;/g, '&')
                  .replace(/¨T/g, '¨')
                  .replace(/¨D/g, '$')
                  .replace(/["']/g, '-')
                  .toLowerCase();
            } else {
                title = title
                  .replace(/[^\w]/g, '')
                  .toLowerCase();
            }

            if (mdOptions.rawPrefixHeaderId) {
                var prefix = '';
                if (_.isString(mdOptions.prefixHeaderId)) {
                    prefix = mdOptions.prefixHeaderId;
                } else if (mdOptions.prefixHeaderId === true) {
                    prefix = 'section-';
                } else {
                    prefix = '';
                }
                title = prefix + title;
            }

            return title;
        }

        self.open = function(file) {
            if (file != null) {
                if (/\.md$/i.test(file.name)) {
                    filename = file.name;
                    // open file
                    xUtil.file.open(file).then(function(content) {
                        codeEditor.setValue(content, -1);
                        codeEditor.resize();
                        self.gotoTop();
                        jQuery('.x-markdown').stop(true).scrollTop(0);
                    });
                } else {
                    xDialog.alert('提示信息', '您打开的不是Markdown文件！');
                }
            }
        }

        self.save = function() {
            xUtil.file.save(filename, codeEditor.getValue());
        }

        // insert code of markdown
        self.insert = function(type, value) {
            var objWrap = null,
                text = codeEditor.getSession().getTextRange(codeEditor.getSelectionRange());
            switch (type) {
                case 'header':
                    var mark = _.map(_.range(value), function() {
                            return '#';
                        }).join('');
                    objWrap = {
                        content: mark + ' ' + text + ' ' + mark,
                        cursor: (mark + ' ').length
                    };
                    break;
                case 'bold':
                    objWrap = { content: '**' + text + '**', cursor: 2 };
                    break;
                case 'italic':
                    objWrap = { content: '*' + text + '*', cursor: 1 };
                    break;
                case 'list':
                    objWrap = {
                        content: _.map(text.split('\n'), function(line) {
                                return '- ' + line;
                            }).join('\n'),
                        cursor: 2
                    };
                    break;
                case 'table':
                    xDialog.open().showTable().then(function(table) {
                        var column = '|' + _.map(_.range(table.columns), function() {
                                return '   ';
                            }).join('|') + '|';
                        var thead = column,
                            divider = '|' + _.map(_.range(table.columns), function() {
                                    return '---';
                                }).join('|') + '|',
                            tbody = _.map(_.range(table.rows), function() {
                                    return column;
                                }).join('\n');
                        codeEditor.insert([thead, divider, tbody].join('\n'));
                    });
                    break;
                case 'link':
                    objWrap = { content: '[超链接](http:// "")', cursor: 13 };
                    text = '';
                    break;
                case 'image':
                    xDialog.open().showImage().then(function(image) {
                        codeEditor.insert('![图片](' + image.url + ' "' + image.name + '")');
                    });
                    break;
                case 'code':
                    xDialog.open().editCode({ text: text }).then(function(data) {
                        codeEditor.insert('```' + data.language + '\n' + data.code + '\n```');
                    });
                    break;
                case 'emoji':
                    xDialog.open().showEmoji().then(function(emoji) {
                        codeEditor.insert(emoji);
                    });
                    break;
                default:
                    break;
            }
            // insert wrap code
            if (objWrap) {
                var objCursor = codeEditor.selection.getCursor();
                codeEditor.insert(objWrap.content);
                if (_.isEmpty(text)) {
                    codeEditor.moveCursorTo(objCursor.row, objCursor.column + objWrap.cursor);
                }
                codeEditor.focus();
            }
        }

        // goto top of editor
        self.gotoTop = function() {
            codeEditor.scrollToLine(0);
        }

        self.callback = function(height, width) {
            jQuery('.x-page-scope').css('height', (height + 45) + 'px');
            jQuery('.x-page-scope .x-editor pre').css('min-height', (height + 45) + 'px');
        }

        self.publish = function() {
            var snapshot = jQuery('.x-markdown>p *').not('svg').text().replace(/\s+/g, ' ');
            if (snapshot.length > 200) {
                snapshot = snapshot.substring(0, 197) + '...';
            }
            xDialog.open().publishBlog({
                blogid: blogid,
                content: self.code,
                snapshot: snapshot,
                modified: modified
            }).then(function(blog) {
                if (blog.id) {
                    filename = blog.title + '.md';
                    blogid = blog.id;
                    modified = blog.modified;
                    if ($location.url().indexOf('blogid=' + blogid) == -1) {
                        $location.path('/markdown-editor', false).search({ blogid: blogid }).replace();
                    }
                }
            });
        }

        self.reset = function() {
            filename = '新建文档.md';
            if (blogid) {
                blogid = null;
                modified = null;
                $location.path('/markdown-editor', false).search({}).replace();
            }
            codeEditor.setValue('', -1);
        }
    });
});
